package com.tinywind.admin.module.system.controller;

import com.tinywind.admin.common.annotation.LogAround;
import com.tinywind.admin.common.base.BaseResult;
import com.tinywind.admin.common.config.shiro.ShiroUtils;
import com.tinywind.admin.common.consts.SystemConst;
import com.tinywind.admin.common.utils.RedisUtil;

import com.tinywind.admin.module.system.entity.SysUserInfo;
import com.tinywind.admin.module.system.service.LoginService;
import com.tinywind.admin.module.system.service.SysLoginLogService;
import com.tinywind.admin.module.system.service.SysRolePermissionService;
import com.tinywind.admin.module.system.service.SysUserRoleService;
import com.tinywind.admin.module.system.vo.MenuVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @author liuxingyu01
 * @date 2021-12-22-20:46
 * @description 系统登录控制器
 **/
@Api(value = "LoginController", description = "系统登录控制器")
@Controller
@RequestMapping("/system")
public class LoginController {
    final static Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private LoginService loginService;

    @Autowired
    private SysLoginLogService sysLoginLogService;

    @Autowired
    private SysUserRoleService sysUserRoleService;

    @Autowired
    private SysRolePermissionService sysRolePermissionService;

    @Autowired
    private RedisUtil redisUtil;


    @LogAround(value = "执行系统用户登陆操作")
    @ApiOperation(value = "执行系统用户登陆操作", notes = "执行系统用户登陆操作")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public BaseResult login(HttpServletRequest request,
                            @RequestParam("username") String username,
                            @RequestParam("password") String password,
                            @RequestParam("code") String code,
                            @RequestParam("uuid") String uuid) {
        String verifyKey = SystemConst.CAPTCHA_CODE_KEY + ":" + uuid;
        String captcha = (String) redisUtil.get(verifyKey);

        if (StringUtils.isNotBlank(captcha) && captcha.equalsIgnoreCase(code)) {
            // 验证成功，删除点缓存
            redisUtil.del(verifyKey);
        } else {
            return BaseResult.failure("验证码错误或已过期，请重新输入!");
        }

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            // 执行登录
            subject.login(token);

            if (logger.isInfoEnabled()) {
                logger.info("LoginController - doLogin - {}登陆成功！ SessionId = {}", username, ShiroUtils.getSessionId());
            }

            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put(SystemConst.SYSTEM_USER_TOKEN, ShiroUtils.getSessionId());
            resultMap.put("user", username);

            // 保存登录日志
            sysLoginLogService.saveLoginlog(request, username, "0", "用户登录成功！", ShiroUtils.getSessionId());
            return BaseResult.success("登录成功，欢迎回来！", resultMap);
        } catch (UnknownAccountException e) {
            sysLoginLogService.saveLoginlog(request, username, "1", "账户不存在！", "");
            return BaseResult.failure("账户不存在！");
        } catch (DisabledAccountException e) {
            sysLoginLogService.saveLoginlog(request, username, "1", "该账户已被冻结！", "");
            return BaseResult.failure("该账户已被冻结！");
        } catch (IncorrectCredentialsException e) {
            sysLoginLogService.saveLoginlog(request, username, "1", "密码错误，请重新输入！", "");
            recordLoginTimes(username);
            return BaseResult.failure("密码错误，请重新输入！");
        } catch (ExcessiveAttemptsException e) {
            sysLoginLogService.saveLoginlog(request, username, "1", "密码连续输入错误超过5次，账号将被锁定半小时！", "");
            redisUtil.expire(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username, 1800);
            return BaseResult.failure("密码连续输入错误超过5次，账号将被锁定半小时！");
        } catch (RuntimeException e) {
            logger.error("LoginController -- doLogin -- RuntimeException = {e}", e);
            sysLoginLogService.saveLoginlog(request, username, "1", "未知错误！！！", "");
            return BaseResult.failure("未知错误！！！");
        }
    }


    /**
     * 当密码错误时，做记录
     *
     * @param username
     */
    private void recordLoginTimes(String username) {
        // 获取当前用户的密码错误次数
        int errorTimes = redisUtil.get(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username) == null ? 0
                : Integer.parseInt((String) redisUtil.get(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username));
        redisUtil.set(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username, String.valueOf(errorTimes + 1), 1800);
    }


    @LogAround(value = "执行初始化用户信息操作")
    @ApiOperation(value = "执行初始化用户信息操作", notes = "执行初始化用户信息操作")
    @RequestMapping(value = "/getUserInfo", method = RequestMethod.GET)
    @ResponseBody
    public BaseResult getUserInfo() {
        SysUserInfo userInfo = ShiroUtils.getSysUserInfo();
        userInfo.setPassword(null);

        Map<String, Object> resultMap = new HashMap<>();
        // 获取用户角色信息
        Set<String> roleSet = sysUserRoleService.listUserRoleByUserId(userInfo.getUserId());
        // 获取用户权限列表
        Set<String> permissionSet = sysRolePermissionService.listRolePermissionByUserId(userInfo.getUserId());

        resultMap.put("permissions", permissionSet);
        resultMap.put("roles", roleSet);
        resultMap.put("userinfo", userInfo);


        return BaseResult.success("获取用户信息成功！", resultMap);
    }


    @LogAround(value = "执行初始化菜单信息操作")
    @ApiOperation(value = "执行初始化菜单信息操作", notes = "执行初始化菜单信息操作")
    @RequestMapping(value = "/initMenu", method = RequestMethod.POST)
    @ResponseBody
    public BaseResult initMenu() throws InterruptedException {
        // 组织菜单列表
        List<MenuVo> menuList = loginService.listMenuByUserId(ShiroUtils.getSysUserId());

        // 模拟延迟两秒，判断前端的错误，暂时用不到了
        // Thread.sleep(2000L);

        return BaseResult.success("获取菜单信息成功！", menuList);
    }


    @LogAround(value = "执行初始化系统信息操作")
    @ApiOperation(value = "执行初始化系统信息操作", notes = "执行初始化系统信息操作")
    @RequestMapping(value = "/getInfo", method = RequestMethod.GET)
    @ResponseBody
    public BaseResult getInfo() {
        SysUserInfo userInfo = ShiroUtils.getSysUserInfo();
        // 将密码置空，防止密码串泄露
        userInfo.setPassword(null);

        // 获取用户角色信息
        Set<String> roleSet = sysUserRoleService.listUserRoleByUserId(userInfo.getUserId());
        // 获取用户权限列表
        Set<String> permissionSet = sysRolePermissionService.listRolePermissionByUserId(userInfo.getUserId());

        Map<String, Object> resultMap = new HashMap<>();

        resultMap.put("username", userInfo.getAccount());
        resultMap.put("permissions", permissionSet);
        resultMap.put("roles", roleSet);
        resultMap.put("userinfo", userInfo);

        return BaseResult.success("获取菜单信息成功！", resultMap);
    }


    @LogAround(value = "执行退出登陆操作")
    @ApiOperation(value = "执行退出登陆操作", notes = "执行退出登陆操作")
    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    @ResponseBody
    public BaseResult logout() {
        ShiroUtils.logout();
        return BaseResult.success("退出登录成功！", null);
    }

}
